import {arrayRemove, objRemove} from '../tools';
import {
  STATE_TODO,
  STATE_HANDLE_NAME_REFRESH,
  STATE_HANDLE_NAME_ADD,
  STATE_HANDLE_NAME_INSERT,
  STATE_HANDLE_NAME_PUSH,
  STATE_HANDLE_NAME_CLEAR,
  STATE_HANDLE_NAME_DEL,
  STATE_HANDLE_NAME_DEL_BY_KEY,
  STATE_HANDLE_NAME_CLEARALL,
  STATE_HANDLE_NAME_SET,
  STATE_HANDLE_NAME_SET_BY_LIST,
  STATE_HANDLE_NAME_UPDATE,
  STATE_HANDLE_NAME_ADD_UPDATE,
  STATE_HANDLE_NAME_UPDATE_MANY,
  STATE_HANDLE_NAME_SET_STATE,
  STATE_HANDLE_NAME_UPDATE_BY_CONDITION
} from '../constants'

export function createReducer(handlerMap, handlers, initState){
  return (state=initState, action)=>{
    let handlerName = handlerMap[action.type];
    if (handlerName){
      if (handlers[handlerName]){
        return handlers[handlerName](state, action)
      } else {
        let actions = handlerName(state, action);
        if (!actions){
          return state
        }
        if (!(actions instanceof Array)){
          actions = [actions];
        }
        let newState = state;
        for (let action of actions){
          if (action[STATE_TODO]){
            newState=handlers[action[STATE_TODO]](newState, action.action)
          } else {
            newState=action
          }
        }
        return newState
      }
    } else {
      return state
    }
  }
}


export let listReducerHandlers = {
    [STATE_HANDLE_NAME_REFRESH]: (state, action)=>{
      return {
        idArrays:{...state.idArrays, [action.key]: action.data.ids}, 
        completeds: {...state.completeds, [action.key]: action.completed}
      }
    },
    [STATE_HANDLE_NAME_ADD]: (state, action)=>{
      return {
        idArrays:{...state.idArrays, [action.key]:[...state.idArrays[action.key], ...action.data.ids]},
        completeds: {...state.completeds, [action.key]: action.completed}
      }
    },
    [STATE_HANDLE_NAME_INSERT]:(state, action)=>{
      if (state.idArrays[action.key]){
        return {
          idArrays:{...state.idArrays, [action.key]:[action.obj.id, ...state.idArrays[action.key]]},
          completeds: state.completeds
        }
      } else {
        return state
      }
    },
    [STATE_HANDLE_NAME_PUSH]: (state, action)=>{
      if (state.idArrays[action.key]){
        return {
          idArrays:{...state.idArrays, [action.key]:[...state.idArrays[action.key], action.obj.id]},
          completeds: state.completeds
        }
      } else {
        return state
      }
    },
    [STATE_HANDLE_NAME_CLEAR]: (state, action)=>{
      return {
        idArrays: objRemove(state.idArrays, action.key),
        completeds: objRemove(state.completeds, action.key)
      }
    },
    [STATE_HANDLE_NAME_DEL]: (state, action)=>{
      let newIdArrays = {};
      for (let key in state.idArrays){
        let idArray = state.idArrays[key];
        newIdArrays[key] = arrayRemove(idArray, action.objId);
      }
      return {idArrays: newIdArrays, completeds: state.completeds};
    },
    [STATE_HANDLE_NAME_DEL_BY_KEY]: (state, action)=>{
      if (state.idArrays[action.key]){
        return {
          idArrays: {...state.idArrays, [action.key]: arrayRemove(state.idArrays[action.key], action.objId)},
          completeds: state.completeds
        }
      } else {
        return state
      }
    },
    [STATE_HANDLE_NAME_CLEARALL]: (state, action)=>{
      return {
        idArrays: [],
        completeds: []
      }
    },
    [STATE_HANDLE_NAME_SET_STATE]: (state, action)=>{
      return action.state
    }
}

export function createListsReducer(callerMap){
  return createReducer(callerMap, listReducerHandlers, {idArrays:{}, completeds:{}})
}


function updateObj(oldObj, obj){
  let newObj = {...oldObj};
  for (attrName in obj){
    let attrValue = obj[attrName];
    let curValue = oldObj[attrName];
    if (typeof(attrValue)==='function'){
      newObj[attrName] = attrValue(curValue)
    } else {
      newObj[attrName] = attrValue;
    }
  }
  return newObj
}

export let objsReducerHandlers={
    [STATE_HANDLE_NAME_SET]: (state, action)=>{
      return {...state, [action.obj.id]: action.obj}
    },
    [STATE_HANDLE_NAME_SET_BY_LIST]: (state, action)=>{
      return {...state, ...action.data.objs}
    },
    [STATE_HANDLE_NAME_DEL]: (state, action)=>{
      return objRemove(state, action.objId)
    },
    [STATE_HANDLE_NAME_CLEARALL]: (state, action)=>{
      return {}
    },
    [STATE_HANDLE_NAME_UPDATE]: (state, action)=>{
      if (state[action.obj.id]){
        return {...state, [action.obj.id]:updateObj(state[action.obj.id], action.obj)}
      } else {
        return state
      }
    },
    [STATE_HANDLE_NAME_UPDATE_MANY]: (state, action)=>{
      let newState = {...state};
      let hasChange = false;
      for (let obj of action.objs) {
        if (state[obj.id]){
          newState[obj.id] = updateObj(state[obj.id], obj);
          hasChange = true;
        }
      }
      if (hasChange){
        return newState
      } else {
        return state 
      }
    },
    [STATE_HANDLE_NAME_UPDATE_BY_CONDITION]: (state, action)=>{
      let newState = {...state};
      let hasChange = false;
      for (let objId in state) {
        let obj = state[objId];
        if (action.isMatch(obj, action)){
          hasChange = true;
          newState[objId] = updateObj(obj, action.update)
        }
      }
      if (hasChange){
        return newState
      } else {
        return state 
      }
    },
    [STATE_HANDLE_NAME_ADD_UPDATE]: (state, action)=>{
      if (action.objs.length > 0){
        let newState = {...state};
        for (let obj of action.objs) {
          if (state[obj.id]){
            newState[obj.id] = updateObj(state[obj.id], obj);
          } else {
            newState[obj.id] = obj;
          }
        }
        return newState
      } else {
        return state
      }
    },
    [STATE_HANDLE_NAME_SET_STATE]: (state, action)=>{
      return action.state
    }
}

export let objReducerHandlers={
    [STATE_HANDLE_NAME_SET]: (state, action)=>{
      return {...state, ...action.obj}
    },
    [STATE_HANDLE_NAME_CLEARALL]: (state, action)=>{
      return {}
    },
    [STATE_HANDLE_NAME_UPDATE]: (state, action)=>{
      return updateObj(state, action.obj)
    },
    [STATE_HANDLE_NAME_SET_STATE]: (state, action)=>{
      return action.state
    }
}

export function createObjsReducer(callerMap){
  return createReducer(callerMap, objsReducerHandlers, {})
}


export function stateUpdatePlus(num=1){
  return (curValue)=>{return curValue+num}
}

export function stateUpdateMinus(num=1){
  return (curValue)=>{return curValue-num}
}

export function stateUpdateInsert(value){
    return (curValue)=>{return [value, ...curValue]}
}

export function stateUpdateAppend(value){
    return (curValue)=>{return [...curValue, value]}
}

export function createObjReducer(callerMap, initState={}){
  return createReducer(callerMap, objReducerHandlers, initState)
}